<div id="app"></div>
<style>
  .box {
    width: 500px;
    min-height: 300px;
    border: 1px solid #eee;
    margin: 10px auto;
    padding: 20px;
  }
</style>
<script>
  //创建虚拟 DOM 节点
  function h(tag, props, children) {
    return { tag, props, children };
  }

  //将虚拟 DOM 节点挂载到真实 DOM 上
  function mount(vnode, container) {
    if (vnode.tag) {
      let el = document.createElement(vnode.tag);
      vnode.el = el; // 虚拟 DOM 节点对应的真实 DOM 节点
      for (let key in vnode.props) {
        if (key === "style") {
          for (let k in vnode.props.style) {
            el.style[k] = vnode.props.style[k];
          }
          continue;
        }
        el.setAttribute(key, vnode.props[key]);
      }
      if (vnode.children) {
        if (typeof vnode.children === "string") {
          // el.appendChild(document.createTextNode(vnode.children));
          el.textContent = vnode.children;
        } else {
          vnode.children.forEach((child) => {
            if (typeof child === "string") {
              el.appendChild(document.createTextNode(child));
            } else {
              mount(child, el);
            }
          });
        }
      }
      container.appendChild(el);
    }
  }

  // 更新, 简单的对比新旧虚拟dom，主要是理解思路
  function path(oldNode, newNode) {
    //下一轮对比，新的虚拟dom就变成了旧的虚拟dom，所以记住新的虚拟dom对应的真实dom
    let el = (newNode.el = oldNode.el);

    // 1. 比较 tag
    if (oldNode.tag === newNode.tag) {
      // props
      const oldNodeProps = oldNode.props || {};
      const newNodeProps = newNode.props || {};
      for (let key in newNodeProps) {
        if (newNodeProps[key] !== oldNodeProps[key]) {
          el.setAttribute(key, newNodeProps[key]); //更新
        }
      }
      for (let key in oldNodeProps) {
        if (!newNodeProps[key]) {
          el.removeAttribute(key); //删除
        }
      }

      // children
      const oldNodeChildren = oldNode.children;
      const newNodeChildren = newNode.children;
      if (typeof newNodeChildren === "string") {
        if (typeof oldNodeChildren === "string") {
          if (newNodeChildren !== oldNodeChildren) {
            el.textContent = newNodeChildren;
          }
        } else {
          el.innerHTML = newNodeChildren;
        }
        return;
      }
      if (typeof oldNodeChildren === "string") {
        el.innerHTML = ""; //清空
        newNodeChildren.forEach((child) => {
          if (typeof child === "string") {
            el.appendChild(document.createTextNode(child));
          } else {
            mount(child, el);
          }
        });
        return;
      }

      // 都是数组对比  这里比较复杂，真实的对比比这个要复杂多, 这里写一个简单的比较
      //1.比较新旧的交集的虚拟dom
      const minLength = Math.min(
        oldNodeChildren.length,
        newNodeChildren.length
      );
      for (let i = 0; i < minLength; i++) {
        let n1 = oldNodeChildren[i];
        let n2 = newNodeChildren[i];
        // //新旧vnode可能是字符串
        // if (typeof n1 === "string") {
        //   n1 = { tag: "text", children: n1, el };
        // }
        //  if (typeof n2 === "string") {
        //   n2 = { tag: "text", children: n2,el };
        // }
        // debugger
        path(n1, n2);
      }
      // 2.新的比旧的多，多的直接挂载
      if (newNodeChildren.length > oldNodeChildren.length) {
        newNodeChildren.slice(oldNodeChildren.length).forEach((child) => {
          if (typeof child === "string") {
            el.appendChild(document.createTextNode(child));
          } else {
            mount(child, el);
          }
        });
      }

      // 3.新的比旧的少，多的直接删除
      if (newNodeChildren.length < oldNodeChildren.length) {
        oldNodeChildren.slice(newNodeChildren.length).forEach((child) => {
          el.removeChild(child.el);
        });
      }
    } else {
      // 替换 replace
      const newEl = document.createElement(newNode.tag);
      el.parentNode.replaceChild(newEl, el);
      newNode.el = newEl;
      mount(newNode, newEl);
    }
  }

  const vdom = h("div", { class: "box" }, [
    h("span", { style: { color: "red" } }, "hello"),
    h("p", { style: { color: "red" } }, "world"),
  ]);
  mount(vdom, document.querySelector("#app"));

  //手动模拟数据更新
  setTimeout(() => {
    console.log(vdom.el);
    const newVdom = h("div", { class: "box" }, [
      h("span", { style: { color: "blue" } }, "牛逼的很，你好"),
      h("div", { style: { color: "blue" } }, "尤大神"),
    ]);
    path(vdom, newVdom);
  }, 2000);
</script>
